// 登录控制器模块
module controller.LoginController;

import std.conv : to;
import std.json : JSONValue, parseJSON;
import core.time : seconds;

import archttp.HttpStatusCode : HttpStatusCode;
import archttp.HttpRequest : HttpRequest;
import archttp.HttpResponse : HttpResponse;
import cachetools : TTL;

import model.User;
import repository.UserRepo;
import utils.RespData : RespData;

import utils.DataCache;
import utils.CacheKey : CacheKey;
import utils.MyJwt : MyJwt;

// import std.stdio;

// 登录控制器类
class LoginController
{
    UserRepo _uRepo; // User仓储

    this ()
    {
        // 注入User仓储
        _uRepo = new UserRepo();
    }

    /** 
     * 登录action
     */
    public void login (HttpRequest req, HttpResponse resp)
    {
        import std.digest : toHexString;
        import std.digest.md : md5Of;
        import std.conv : to;
        import std.datetime.systime : Clock;

        import utils.ConstValue : Const;

        string ip = req.ip();

        // 缓存中有ip记录
        auto data = cacheData.get(ip);
        if (!data.isNull)
        {
            uint count = to!uint(data.get);
            // 超过次数限制直接拒绝
            if ((count + 1) >= 6)
            {
                resp.json(RespData.newData(403, "您的操作过于频繁，请稍后再试。")).send();
                return;
            }
            // 没超过次数限制
            // 设置缓存，记录IP
            cacheData.put(ip, to!string(count + 1), TTL(180.seconds));
        }
        else
        {
            // 缓存中没有ip记录
            // 设置缓存，记录IP
            cacheData.put(ip, to!string(1), TTL(180.seconds));
        }

        // 接收前端数据,反序列化
        string bodyStr = req.body();
        if (bodyStr == "" || bodyStr is null)
        {
            auto respData = RespData.newData(400, "错误的请求");
            resp.code(HttpStatusCode.BAD_REQUEST).json(respData).send();
            return;
        }
        JSONValue json = parseJSON(bodyStr);
        string username = json["username"].str;
        string plainPwd = json["password"].str;
        if (username == "" || plainPwd == "")
        {
            resp.code(HttpStatusCode.BAD_REQUEST).json(RespData.newData(400, "错误的请求")).send();
            return;
        }
        // 明文密码md5加密
        string pwd = to!string(toHexString(md5Of(Const.saltStr ~ plainPwd)));
        // 验证是否查询到记录
        User user = _uRepo.getOneUser(username, pwd);
        if (user is null) // 没有记录
        {
            resp.code(HttpStatusCode.BAD_REQUEST).json(RespData.newData(400, "用户名或密码错误")).send();
            return;
        }

        // 生成jwt token
        long exp = Clock.currTime.toUnixTime() + 7200;
        JSONValue userdata = [
            "id" : JSONValue(user.id),
            "username" : JSONValue(user.username),
            "nickname" : JSONValue(user.nickname),
            "exp" : JSONValue(exp)
        ];
        string token = MyJwt.createToken(userdata);

        JSONValue userInfo = ["username" : user.username, "token" : token];
        userInfo.object["userid"] = JSONValue(user.id);
        userInfo.object["role"] = JSONValue(user.role);
        resp.json(RespData.newData(200, "ok", userInfo)).send();
    }

    /** 
     * 退出登录action
     */
    public void logout (HttpRequest req, HttpResponse resp)
    {
        import std.datetime.systime : Clock;

        auto headers = req.headers();
        if ("authorization" !in headers)
        {
            resp.json(RespData.newData(401, "请登录")).send();
            return;
        }
        string token = headers["authorization"][7 .. $];

        if (!MyJwt.validateToken(token))
        {
            resp.json(RespData.newData(401, "token验证失败")).send();
            return;
        }

        auto tokenData = MyJwt.decodeToken(token);
        auto exp = tokenData.claims.exp;
        long now = Clock.currTime.toUnixTime();
        if (exp <= now)
        {
            resp.json(RespData.newData(401, "token已过期")).send();
            return;
        }

        auto jwtUser = tokenData.claims.getObject("user");
        if (jwtUser is null)
        {
            resp.json(RespData.newData(401, "token不正确")).send();
            return;
        }

        resp.json(RespData.newData(200, "ok")).send();
    }

}
